Skip to content

pref:增加事件流字段,修复事件流信息缺口并清理废弃代码#135

Merged
minorcell merged 4 commits into
1024XEngineer:mainfrom
phantom5099:main
Apr 3, 2026
Merged

pref:增加事件流字段,修复事件流信息缺口并清理废弃代码#135
minorcell merged 4 commits into
1024XEngineer:mainfrom
phantom5099:main

Conversation

@phantom5099

@phantom5099 phantom5099 commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

变更说明

本次 PR 增加返回事件流定义字段,同时解决了事件流架构中的两个问题:

  1. 清理已废弃的 mergeToolCallDeltas 函数及相关测试
  2. 修复 tool_call_start 事件缺少 ToolCallIndex 的信息缺口

问题背景

在事件流架构中,Provider 通过 StreamEvent 向 Runtime 推送增量事件。原先的实现中:

  • tool_call_start 事件只包含 IDName,缺少 Index
  • tool_call_delta 事件只包含 IndexArgumentsDelta,缺少 ID

这导致两个事件无法关联,Runtime 无法从事件流重建完整的 Message

主要改动

1. 删除废弃函数(openai.go

删除 mergeToolCallDeltas 函数(第 437-463 行),该函数已被 consumeStream 内联处理替代。

- func mergeToolCallDeltas(target map[int]*domain.ToolCall, deltas []toolCallDelta) []domain.ToolCall {
-     // ... 27 行代码
- }

2. 修复事件流信息缺口

修改 emitToolCallStart 函数签名,新增 index 参数:

- func emitToolCallStart(ctx context.Context, events chan<- domain.StreamEvent, id, name string) error {
+ func emitToolCallStart(ctx context.Context, events chan<- domain.StreamEvent, index int, id, name string) error {
      select {
      case events <- domain.StreamEvent{
          Type:          domain.StreamEventToolCallStart,
+         ToolCallIndex: index,
          ToolCallID:    id,
          ToolName:      name,
-         ToolCallIndex: -1,
      }:

更新调用处(openai.go 第 255 行):

- if err := emitToolCallStart(ctx, events, delta.ID, delta.Function.Name); err != nil {
+ if err := emitToolCallStart(ctx, events, delta.Index, delta.ID, delta.Function.Name); err != nil {

3. 优化事件结构(types.go

重新组织 StreamEvent 字段顺序,按事件类型分组:

type StreamEvent struct {
    Type StreamEventType

    // text_delta
    Text string `json:"text,omitempty"`

    // tool_call_start / tool_call_delta
    ToolCallIndex      int    `json:"tool_call_index,omitempty"`
    ToolCallID         string `json:"tool_call_id,omitempty"`
    ToolName           string `json:"tool_name,omitempty"`
    ToolArgumentsDelta string `json:"tool_arguments_delta,omitempty"`

    // message_done
    FinishReason string `json:"finish_reason,omitempty"`
    Usage        *Usage `json:"usage,omitempty"`
}

更新字段注释,明确 ToolCallIndextool_call_starttool_call_delta 中都有效。

4. 清理不必要的字段设置

移除 emitTextDeltaemitMessageDone 中不必要的 ToolCallIndex 字段设置:

  func emitTextDelta(...) {
      events <- domain.StreamEvent{
          Type: domain.StreamEventTextDelta,
          Text: text,
-         ToolCallIndex: -1,
      }
  }

5. 更新测试(openai_test.go

删除废弃测试:

  • TestMergeToolCallDeltas(约 77 行)
  • TestMergeToolCallDeltasEdgeCases(约 122 行)

更新 TestEmitToolCallStartGuards 测试:

- if err := emitToolCallStart(ctx, nil, "call-1", "filesystem_edit"); err != nil {
+ if err := emitToolCallStart(ctx, nil, 0, "call-1", "filesystem_edit"); err != nil {

  // 验证 ToolCallIndex
+ if got.ToolCallIndex != 2 {
+     t.Fatalf("expected ToolCallIndex %d, got %d", 2, got.ToolCallIndex)
+ }

更新 TestProviderChatEmitsFullEventStream 测试:

  case domain.StreamEventToolCallStart:
-     if evt.ToolCallIndex != -1 {
-         t.Fatalf("expected ToolCallIndex %d for tool_call_start, got %d", -1, evt.ToolCallIndex)
+     if evt.ToolCallIndex != 0 {
+         t.Fatalf("expected ToolCallIndex %d for tool_call_start, got %d", 0, evt.ToolCallIndex)
      }

移除对 text_deltamessage_done 事件中 ToolCallIndex 的检查(不再需要验证 -1)。

预期收益

1. 代码质量提升

  • 删除约 180 行废弃代码和测试
  • 减少维护负担,避免误导后续开发者
  • 事件结构更清晰,字段按事件类型分组

2. 事件流信息完整

修复后的 tool_call_start 事件:

StreamEvent{
    Type:          StreamEventToolCallStart,
    ToolCallIndex: 0,              // ✅ 新增:可用于关联
    ToolCallID:    "call_123",
    ToolName:      "edit_file",
}

现在 Runtime 可以:

  1. tool_call_start 创建 ToolCall 条目,用 ToolCallIndex 作为 map key
  2. tool_call_delta 根据 ToolCallIndex 找到对应条目并追加 arguments
  3. 完整重建 Message{Content, ToolCalls}

3. 为未来架构演进铺路

本次修复为"删除 ChatResponse 返回值"的重构奠定了基础:

  • 当前:Provider 组装 ChatResponse,Runtime 直接使用 resp.Message
  • 未来:Provider 只推送事件,Runtime 从事件流重建 Message

架构演进路径:

┌─────────────────────────────────────────┐
│ 阶段 1(当前)                           │
│ Provider: 解析 + 组装 + 推送事件          │
│ Runtime:  直接使用 resp.Message          │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 阶段 2(已具备条件)                     │
│ Provider: 解析 + 推送事件(完整信息)     │
│ Runtime:  累积事件 + 重建 Message         │
│           仍返回 ChatResponse            │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 阶段 3(未来可选)                       │
│ Provider: 解析 + 推送事件                 │
│ Runtime:  累积事件 + 重建 Message         │
│ 接口:    Chat() 只返回 error             │
└─────────────────────────────────────────┘

4. 事件语义更清晰

修复前后的对比:

方面 修复前 修复后
tool_call_start 携带 index ❌ 强制 -1 ✅ 真实 index
事件关联能力 ❌ 无法关联 ✅ 通过 index 关联
Runtime 能否重建 Message ❌ 信息不完整 ✅ 信息完整
字段注释准确性 ❌ 误导性注释 ✅ 准确描述

测试覆盖

  • go build ./... 编译通过
  • ✅ 现有测试全部通过
  • ✅ 新增对 ToolCallIndex 的验证

FIxes: #134

@codecov

codecov Bot commented Apr 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.00000% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/provider/openai/openai.go 70.00% 7 Missing and 5 partials ⚠️

📢 Thoughts on this report? Let us know!

@minorcell minorcell merged commit 25fc2ad into 1024XEngineer:main Apr 3, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pref: 事件流信息缺口与废弃函数清理

2 participants